﻿namespace IBTrader.Indicator
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    abstract class BaseItems<T>
    {
        protected readonly T[] list;
        private readonly int size;
        private readonly SortedItems sorted;
        private readonly Func<T, double> val;
        internal int Index { get; private set; }
        internal bool Init { get; private set; }
        internal int Filled { get; private set; }
        internal double Sum { get; private set; }
        public BaseItems(int size, Func<T, double> val)
        {
            this.val = val;
            this.size = size;
            this.Index = -1;
            list = new T[size];
            sorted = new SortedItems(val);
        }
        internal void Clear()
        {
            Index = -1;
            Init = false;
            Filled = 0;
            Sum = 0;
            sorted.Clear();
        }
        internal T Add(T t)
        {
            if (!Init && Index == size - 1) Init = true;
            Index = (Index + 1) % size;

            if (Init) Sum -= val(list[Index]);
            if (Init) Filled--;
            if (Init) sorted.Remove(list[Index]);

            list[Index] = t;

            Sum += val(t);
            Filled++;
            sorted.Add(t);
            return t;
        }
        internal void AddNew(T t)
        {
            if (!Contains(t))
                Add(t);
        }
        internal T Min()
        {
            return sorted.First();
        }
        internal T Max()
        {
            return sorted.Last();
        }
        internal T First()
        {
            return list[(Index + 1) % size];
        }
        internal T Last()
        {
            return list[Index];
        }
        internal IEnumerable<T> Min(int cnt)
        {
            return sorted.Take(cnt);
        }
        internal IEnumerable<T> Max(int cnt)
        {
            return sorted.Skip(sorted.Count - cnt);
        }
        internal IEnumerable<T> Last(int cnt)
        {
            for (int i = cnt - 1; i >= 0; i--)
                yield return list[(Index - i + size) % size];
        }
        internal double Average()
        {
            return Filled == 0 ? 0 : (Sum / Filled);
        }
        internal bool Contains(T t)
        {
            return list.Contains(t);
        }
        internal int Length
        {
            get
            {
                return size;
            }
        }
        internal int Count()
        {
            return Init ? Length : Index + 1;
        }
        internal T this[int index]
        {
            get
            {
                var off = Length - 1 - index;
                var relPos = Index - off;
                var pos = (relPos + Length) % Length;
                return list[pos];
            }
        }
        //private class SortedItems : LinkedList<T>
        //{
        //    private Func<T, double> val;
        //    public SortedItems(Func<T, double> val)
        //    {
        //        this.val = val;
        //    }
        //    internal void Add(T t)
        //    {
        //        LinkedListNode<T> last = null;
        //        var curr = this.First;
        //        while (curr != null && val(curr.Value) < val(t))
        //        {
        //            last = curr;
        //            curr = curr.Next;
        //        }
        //        if (last != null)
        //            AddAfter(last, t);
        //        else
        //            AddFirst(t);
        //    }
        //}
        private class SortedItems : List<T>
        {
            private readonly Func<T, double> val;
            public SortedItems(Func<T, double> val)
            {
                this.val = val;
            }
            public new void Add(T t)
            {
                var count = Count / 2;
                var index = count;
                while (count > 1)
                {
                    count /= 2;
                    if (val(this[index]) > val(t)) index -= count;
                    else if (val(this[index]) < val(t)) index += count;
                }
                while (index > 0 && val(this[index - 1]) > val(t)) index--;
                while (index < Count && val(this[index]) < val(t)) index++;

                base.Insert(index, t);
            }
        }
        public IEnumerable<T> GetEnumerator()
        {
            return list;
        }
    }
}